package com.cloud.school.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.cloud.school.app.entity.TbUser;
import com.cloud.school.app.mapstruct.IUserConvertMapper;
import com.cloud.school.app.model.CollegeModel;
import com.cloud.school.app.model.ImageModel;
import com.cloud.school.app.model.MajorModel;
import com.cloud.school.app.model.UserModel;
import com.cloud.school.app.repository.TbUserMapper;
import com.cloud.school.app.service.CollegeService;
import com.cloud.school.app.service.ImageService;
import com.cloud.school.app.service.MajorService;
import com.cloud.school.app.service.UserService;
import com.cloud.school.app.util.IdWorker;
import com.cloud.school.app.util.enumeration.UserStatus;
import com.cloud.school.app.util.exception.SystemHandleException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName UserService
 * @Description TODO
 * @Author 36297
 * @Date 2018/12/2 23:06
 * @Version 1.0
 */
@Service
public class UserServiceImpl implements UserService {

    public static final String USER_ID_USER_INFO_CACHE = "userIdUserInfoCache.";
    public static final int expireTime = 12;
    private static final Random r = new Random();
    private static final long[] male = {242271247596953600l, 249433386631692288l, 249433432479629312l};
    private static final long[] female = {242272925264027650l, 249433461642625024l, 249433476494655488l};

    @Autowired
    private TbUserMapper tbUserMapper;
    @Autowired
    private IUserConvertMapper userConvertMapper;
    @Autowired
    private IdWorker userIdWorker;
    @Autowired
    private CollegeService collegeService;
    @Autowired
    private ImageService imageService;
    @Autowired
    private MajorService majorService;
    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional
    public boolean save(UserModel userModel) {
        if (userModel.getCellPhoneNum() == null) {
            throw new SystemHandleException("用户手机号不能为空");
        }
        if (isPhoneNumExist(userModel.getCellPhoneNum())) {
            throw new SystemHandleException("用户手机号重复");
        }
        if (userModel.getImage() == null) {
            if (userModel.getGender() == null || 2 == userModel.getGender().intValue()) {
                userModel.setGender(2);
                userModel.setImage(male[r.nextInt(3)]);
            } else if (1 == userModel.getGender().intValue()) {
                userModel.setImage(female[r.nextInt(3)]);
            }
        }
        TbUser user = userConvertMapper.modelToEntity(userModel);
        user.setId(userIdWorker.nextId());
        return tbUserMapper.insertSelective(user) > 0;
    }

    @Override
    public boolean update(UserModel userModel) {
        if (userModel.getId() == null) {
            throw new SystemHandleException("用户id不能为空");
        }
        cacheService.delete(USER_ID_USER_INFO_CACHE + userModel.getId());
        return tbUserMapper.updateByPrimaryKeySelective(userConvertMapper.modelToEntity(userModel)) > 0;
    }

    @Override
    public UserModel queryById(Long userId) {
        String userString = cacheService.get(USER_ID_USER_INFO_CACHE + userId);
        UserModel userModel = null;
        if (!StringUtils.isEmpty(userString)) {
            userModel = JSON.parseObject(userString, UserModel.class);
            cacheService.expire(USER_ID_USER_INFO_CACHE + userId, expireTime, TimeUnit.HOURS);
        } else {
            userModel = userConvertMapper.entityToModel(tbUserMapper.selectByPrimaryKey(userId));
            if (userModel == null) {
                return null;
            }
            List<UserModel> userModels = Lists.newArrayList(userModel);
            appendImage(userModels);
            cacheService.put(USER_ID_USER_INFO_CACHE + userId, JSON.toJSONString(userModel), expireTime, TimeUnit.HOURS);
        }
        return userModel;
    }

    @Override
    public UserModel queryDetailById(Long queryById) {
        UserModel userModel = queryById(queryById);
        if (userModel == null) {
            return null;
        }
        List<UserModel> userModels = Lists.newArrayList(userModel);
        appendMajor(userModels);
        appendCollege(userModels);
        return userModel;
    }

    @Override
    public List<UserModel> queryByIds(List<Long> userIds) {
        List<Long> cacheUids = userIds.stream().filter(id -> cacheService.hasKey(USER_ID_USER_INFO_CACHE + id)).distinct().collect(Collectors.toList());
        List<UserModel> userModels = null;
        if (!CollectionUtils.isEmpty(cacheUids)) {
            userModels = cacheUids.stream().map(id -> cacheService.get(USER_ID_USER_INFO_CACHE + id)).map(s -> JSON.parseObject(s, UserModel.class)).collect(Collectors.toList());
            userModels.forEach(u -> cacheService.expire(USER_ID_USER_INFO_CACHE + u.getId(), expireTime, TimeUnit.HOURS));
        } else {
            userModels = Lists.newArrayList();
        }
        List<Long> noCacheUids = userIds.stream().filter(id -> !cacheService.hasKey(USER_ID_USER_INFO_CACHE + id)).distinct().collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(noCacheUids)) {
            List<UserModel> noCacheUserModels = userConvertMapper.entityToModel(tbUserMapper.selectByIds(String.join(",", noCacheUids.stream().map(id -> id.toString()).collect(Collectors.toList()))));
            appendImage(noCacheUserModels);
            noCacheUserModels.forEach(u -> cacheService.put(USER_ID_USER_INFO_CACHE + u.getId(), JSON.toJSONString(u), expireTime, TimeUnit.HOURS));
            userModels.addAll(noCacheUserModels);
        }
        return userModels;
    }

    @Override
    public List<UserModel> queryDetailByIds(List<Long> userIds) {
        List<UserModel> userModels = queryByIds(userIds);
        appendMajor(userModels);
        appendCollege(userModels);
        return userModels;
    }

    @Override
    public List<UserModel> queryByCollegeIdAndMajorId(Long collegeId, Long majorId) {
        TbUser user = new TbUser();
        user.setCollegeId(collegeId);
        user.setMajorId(majorId);
        List<UserModel> userModels = userConvertMapper.entityToModel(tbUserMapper.select(user));
        appendImage(userModels);
        return userModels;
    }

    private void appendImage(List<UserModel> userModels) {
        List<Long> imageIds = userModels.stream().map(u -> u.getImage()).filter(i -> i != null).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(imageIds)) {
            Map<Long, ImageModel> imageModelMap = imageService.seleteByIds(imageIds).stream().collect(Collectors.toMap(ImageModel::getId, i -> i));
            userModels.forEach(u -> u.setImageModel(imageModelMap.get(u.getImage())));
        }
    }

    private void appendMajor(List<UserModel> userModels) {
        List<Long> majorIds = userModels.stream().map(u -> u.getMajorId()).filter(id -> id != null).collect(Collectors.toList());
        if (!StringUtils.isEmpty(majorIds)) {
            Map<Long, MajorModel> majorModels = majorService.queryByIds(majorIds).stream().collect(Collectors.toMap(m -> m.getId(), m -> m));
            userModels.forEach(u -> u.setMajorModel(majorModels.get(u.getMajorId())));
        }
    }

    private void appendCollege(List<UserModel> userModels) {
        List<Long> collegeIds = userModels.stream().map(u -> u.getCollegeId()).filter(id -> id != null).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(collegeIds)) {
            Map<Long, CollegeModel> collegeModels = collegeService.queryByIds(collegeIds).stream().collect(Collectors.toMap(c -> c.getId(), c -> c));
            userModels.forEach(u -> u.setCollegeModel(collegeModels.get(u.getCollegeId())));
        }
    }

    @Override
    public UserModel queryByPhoneNum(String cellPhoneNum) {
        TbUser user = new TbUser();
        user.setCellPhoneNum(cellPhoneNum);
        user.setStatus(UserStatus.REGISTER.getCode());
        return userConvertMapper.entityToModel(tbUserMapper.selectOne(user));
    }

    @Override
    public boolean isNickNameExist(String nickName) {
        TbUser user = new TbUser();
        user.setNickName(nickName);
        return tbUserMapper.selectCount(user) > 0;
    }

    @Override
    public boolean isPhoneNumExist(String cellPhoneNum) {
        TbUser user = new TbUser();
        user.setCellPhoneNum(cellPhoneNum);
        return tbUserMapper.selectCount(user) > 0;
    }

    @Override
    public Map<Long, Long> queryCollegeMajarRegisterNum(Long collegeId) {
        List<Map<Long, Long>> rs = tbUserMapper.selectCountByCollegeIdGroupByMajor(collegeId);
        Map<Long, Long> result = Maps.newHashMap();
        rs.forEach(m -> result.put(m.get("major_id"), m.get("user_num")));
        return result;
    }
}
